package ex55.mm.flow.impound.quote.common.kit;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

public class ThreadKit {
    private final static Logger logger = LoggerFactory.getLogger(ThreadKit.class);


    private final static ExecutorService executorService = Executors.newFixedThreadPool(100);

    public static <T> Future<T> submitTask(Callable<T> task) {
        return executorService.submit(task);
    }

    public static Future<?> submitTask(Runnable task) {
        return executorService.submit(task);
    }

    public static BasicThreadFactory newNameThreadFactory(String poolName) {
        return new BasicThreadFactory
                .Builder()
                .namingPattern("UT-" + poolName + "_%d")
                .build();
    }

    private final static AtomicLong NAME_THREAD_SEQ = new AtomicLong(0);

    public static Thread newNameThread(String threadName, Runnable runnable) {
        Thread thread = new Thread(runnable);
        thread.setName("UT-" + threadName + "_" + NAME_THREAD_SEQ.incrementAndGet());
        return thread;
    }


    public static void newScheduleSeconds(String poolName, Runnable runnable, long delay) {
        newScheduleSeconds(poolName, runnable, 1, delay);
    }

    public static void newScheduleSeconds(String poolName, Runnable runnable, long initialDelay, long delay) {
        Executors.newSingleThreadScheduledExecutor(newNameThreadFactory("schedule_second_" + poolName))
                .scheduleWithFixedDelay(() -> {
                    try {
                        runnable.run();
                    } catch (Exception e) {
                        logger.warn("poolName:{},error:{}", poolName, e);
                    }
                }, initialDelay, delay, TimeUnit.SECONDS);
    }


    public static void secondsSleepInterrupt(long timeOut) {
        try {
            TimeUnit.SECONDS.sleep(timeOut);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void millisecondsSleepInterrupt(long timeOut) {
        try {
            TimeUnit.MILLISECONDS.sleep(timeOut);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static <T> T tryResult(Callable<T> c) {
        try {
            return c.call();
        } catch (Throwable e) {
            logger.warn("try run error", e);
        }
        return null;
    }

    public static void tryVoid(Runnable c) {
        try {
            c.run();
        } catch (Throwable e) {
            logger.warn("try run error", e);
        }
    }

    public static void newScheduleAtUTC0(String name, Runnable runnable) {
        DateTime n = DateTime.now(DateTimeZone.UTC);
        DateTime next = n.withTimeAtStartOfDay().plusDays(1);
        long i = next.getMillis() - n.getMillis();
        if (i <= 0) {
            i = 0;
        }
        Executors.newSingleThreadScheduledExecutor(newNameThreadFactory("ScheduleAtUTC0:" + name))
                .scheduleAtFixedRate(() -> {
                    try {
                        runnable.run();
                    } catch (Exception e) {
                        logger.warn("name: {} schedule at utc0 error: {}", Thread.currentThread().getName(), e);
                    }
                }, i, TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS);
    }

    public static void newScheduleAtHour0Minute0(Class clazz, Runnable runnable) {
        DateTime n = DateTime.now(DateTimeZone.UTC);
        DateTime next = n
                .withMinuteOfHour(0)
                .withSecondOfMinute(0)
                .withMillisOfSecond(0)
                .plusHours(1);
        long i = next.getMillis() - n.getMillis();
        if (i <= 0) {
            i = 0;
        }
        Executors.newSingleThreadScheduledExecutor(newNameThreadFactory("ScheduleAtHour0Minute0:" + clazz.getCanonicalName()))
                .scheduleAtFixedRate(() -> {
                    try {
                        runnable.run();
                    } catch (Exception e) {
                        logger.warn("name: {} schedule at Hour0Minute0 error: {}", Thread.currentThread().getName(), e);
                    }
                }, i, TimeUnit.HOURS.toMillis(1), TimeUnit.MILLISECONDS);
    }
}
